﻿#ifndef CFRAMELESSWINDOW_H
#define CFRAMELESSWINDOW_H
#include "qsystemdetection.h"
#include <QMainWindow>
#include <QObject>

// A nice frameless window for both Windows and OS X
// Author: Bringer-of-Light
// Github: https://github.com/Bringer-of-Light/Qt-Nice-Frameless-Window
//  Usage: use "CFramelessWindow" as base class instead of "QMainWindow", and enjoy
#ifdef Q_OS_WIN
#    include <QList>
#    include <QMargins>
#    include <QRect>
#    include <QWidget>
#    include <dwmapi.h>
#    include <windows.h>
#    include <windowsx.h>
class CFramelessWindow : public QWidget
{
	Q_OBJECT
public:
	explicit CFramelessWindow(QWidget* parent = 0);

public:
	// 设置是否可以通过鼠标调整窗口大小
	// if resizeable is set to false, then the window can not be resized by mouse
	// but still can be resized programtically
	void setResizeable(bool resizeable = true);
	bool isResizeable() { return m_bResizeable; }

	// 设置可调整大小区域的宽度，在此区域内，可以使用鼠标调整窗口大小
	// set border width, inside this aera, window can be resized by mouse
	void setResizeableAreaWidth(int width = 5);

protected:
	// 设置一个标题栏widget，此widget会被当做标题栏对待
	// set a widget which will be treat as SYSTEM titlebar
	void setTitleBar(QWidget* titlebar);

	// 在标题栏控件内，也可以有子控件如标签控件“label1”，此label1遮盖了标题栏，导致不能通过label1拖动窗口
	// 要解决此问题，使用addIgnoreWidget(label1)
	// generally, we can add widget say "label1" on titlebar, and it will cover the titlebar under it
	// as a result, we can not drag and move the MainWindow with this "label1" again
	// we can fix this by add "label1" to a ignorelist, just call addIgnoreWidget(label1)
	void addIgnoreWidget(QWidget* widget);

	bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result);
private slots:
	void onTitleBarDestroyed();

private:
	QWidget* m_titlebar;
	QList<QWidget*> m_whiteList;
	int m_borderWidth;

	QMargins m_margins;
	QMargins m_frames;
	bool m_bJustMaximized;

	bool m_bResizeable;
};

#elif defined Q_OS_MAC
#    include <QMouseEvent>
#    include <QPoint>
#    include <QResizeEvent>
class CFramelessWindow : public QMainWindow
{
	Q_OBJECT
public:
	explicit CFramelessWindow(QWidget* parent = 0);

private:
	void initUI();

public:
	// 设置可拖动区域的高度，在此区域内，可以通过鼠标拖动窗口, 0表示整个窗口都可拖动
	// In draggable area, window can be moved by mouse, (height = 0) means that the whole window is draggable
	void setDraggableAreaHeight(int height = 0);

	// 只有OS X10.10及以后系统，才支持OS X原生样式包括：三个系统按钮、窗口圆角、窗口阴影
	// 类初始化完成后，可以通过此函数查看是否已经启用了原生样式。如果未启动，需要自定义关闭按钮、最小化按钮、最大化按钮
	// Native style（three system button/ round corner/ drop shadow） works only on OS X 10.10 or later
	// after init, we should check whether NativeStyle is OK with this function
	// if NOT ok, we should implement close button/ min button/ max button ourself
	bool isNativeStyleOK() { return m_bNativeSystemBtn; }

	// 如果设置setCloseBtnQuit(false)，那么点击关闭按钮后，程序不会退出，而是会隐藏,只有在OS X 10.10 及以后系统中有效
	// if setCloseBtnQuit(false), then when close button is clicked, the application will hide itself instead of quit
	// be carefull, after you set this to false, you can NOT change it to true again
	// this function should be called inside of the constructor function of derived classes, and can NOT be called more than once
	// only works for OS X 10.10 or later
	void setCloseBtnQuit(bool bQuit = true);

	// 启用或禁用关闭按钮，只有在isNativeStyleOK()返回true的情况下才有效
	// enable or disable Close button, only worked if isNativeStyleOK() returns true
	void setCloseBtnEnabled(bool bEnable = true);

	// 启用或禁用最小化按钮，只有在isNativeStyleOK()返回true的情况下才有效
	// enable or disable Miniaturize button, only worked if isNativeStyleOK() returns true
	void setMinBtnEnabled(bool bEnable = true);

	// 启用或禁用zoom（最大化）按钮，只有在isNativeStyleOK()返回true的情况下才有效
	// enable or disable Zoom button(fullscreen button), only worked if isNativeStyleOK() returns true
	void setZoomBtnEnabled(bool bEnable = true);

	bool isCloseBtnEnabled() { return m_bIsCloseBtnEnabled; }
	bool isMinBtnEnabled() { return m_bIsMinBtnEnabled; }
	bool isZoomBtnEnabled() { return m_bIsZoomBtnEnabled; }

protected:
	void mousePressEvent(QMouseEvent* event);
	void mouseReleaseEvent(QMouseEvent* event);
	void mouseMoveEvent(QMouseEvent* event);

private:
	int m_draggableHeight;
	bool m_bWinMoving;
	bool m_bMousePressed;
	QPoint m_MousePos;
	QPoint m_WindowPos;
	bool m_bCloseBtnQuit;
	bool m_bNativeSystemBtn;
	bool m_bIsCloseBtnEnabled, m_bIsMinBtnEnabled, m_bIsZoomBtnEnabled;

	//===============================================
	// TODO
	// 下面的代码是试验性质的
	// tentative code

	// 窗口从全屏状态恢复正常大小时，标题栏又会出现，原因未知。
	// 默认情况下，系统的最大化按钮(zoom button)是进入全屏，为了避免标题栏重新出现的问题，
	// 以上代码已经重新定义了系统zoom button的行为，是其功能变为最大化而不是全屏
	// 以下代码尝试，每次窗口从全屏状态恢复正常大小时，都再次进行设置，以消除标题栏
	// after the window restore from fullscreen mode, the titlebar will show again, it looks like a BUG
	// on OS X 10.10 and later, click the system green button (zoom button) will make the app become fullscreen
	// so we have override it's action to "maximized" in the CFramelessWindow Constructor function
	// but we may try something else such as delete the titlebar again and again...
private:
	bool m_bTitleBarVisible;

	void setTitlebarVisible(bool bTitlebarVisible = false);
	bool isTitlebarVisible() { return m_bTitleBarVisible; }
private slots:
	void onRestoreFromFullScreen();
signals:
	void restoreFromFullScreen();

protected:
	void resizeEvent(QResizeEvent* event);
};
#endif

#endif   // CFRAMELESSWINDOW_H
